package pub.tbc.toolkit.io;

import lombok.extern.slf4j.Slf4j;
import pub.tbc.toolkit.Closes;
import pub.tbc.toolkit.Objs;

import java.io.*;

/**
 * 小文件读写操作工具<br>
 * 注：文件过大会导致内存溢出，此工具未做相关防范处理
 *
 * @author tbc on 2016/11/25 0:34.
 */
@Slf4j
public final class FileHelper {
    private FileHelper() {
    }

    private static final String OUT_CONTENT_NULL = "输出内容不能为空";

    private static void proException(String msg, Exception e) {
        msg = msg + " : " + e.getMessage();
        log.error(msg);
        throw new RuntimeException(msg, e);
    }

    private static void proException(Exception e) {
        proException("", e);
    }


    /**
     * 加载文件
     *
     * @param fileName 完整文件名
     * @return File
     */
    public static File loadFile(String fileName) {
        return new File(fileName);
    }

    /**
     * 从文件中提取[字节]输入流
     *
     * @param file File
     * @return InputStream
     */
    public static InputStream getInputStream(File file) {
        InputStream in = null;
        try {
            in = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            proException("加载文件流失败 - 生成FileInputStream失败", e);
        }
        return in;
    }

    /**
     * 加载文件，并提取字节流
     *
     * @param fileName 完整文件路径
     * @return InputStream
     */
    public static InputStream getInputStream(String fileName) {
        return getInputStream(loadFile(fileName));
    }

    /**
     * 读取字节流，转换为String
     *
     * @param inputStream in
     * @return String
     */
    public static String readToString(InputStream inputStream) {
        StringBuilder sb = new StringBuilder();
        byte[] bs = new byte[1024];
        while (true) {
            try {
                int n = inputStream.read(bs);
                if (n <= 0) break;
                sb.append(new String(bs));
            } catch (IOException e) {
                proException("读取文件流失败", e);
            }
        }
        return sb.toString();
    }

    /**
     * 使用默认UTF-8编码加载并读取文件内容，并转换为String
     *
     * @param fileName 完整文件路径
     * @return String
     */
    public static String readToStringByCharset(String fileName) {
        return readToStringByCharset(getInputStream(fileName), "utf-8");
    }

    /**
     * 使用默认UTF-8编码读取文件内容，并转换为String
     *
     * @param file 完整文件路径
     * @return String
     */
    public static String readToStringByCharset(File file) {
        return readToStringByCharset(getInputStream(file), "utf-8");
    }

    /**
     * 使用指定编码读取字节输入流并转换为String
     *
     * @param inputStream byte-inputStream
     * @param charset     编码
     * @return String
     */
    public static String readToStringByCharset(InputStream inputStream, String charset) {
        Reader reader = null;
        try {
            reader = new InputStreamReader(inputStream, charset);
        } catch (UnsupportedEncodingException e) {
            proException("不被支持的编码类型", e);
        }
        assert reader != null : "Assert : reader is null";
        BufferedReader bf = new BufferedReader(reader);

        StringBuilder sb = new StringBuilder();
        while (true) {
            try {
                String str = bf.readLine();
                if (str == null) break;
                sb.append(str);
            } catch (IOException e) {
                proException("IO异常", e);
            }
        }
        return sb.toString();
    }

    /**
     * 将内容写入到文件
     *
     * @param content  待写字符串
     * @param fileName 文件名
     * @return 是否操作成功
     */
    public static boolean writeFile(String content, String fileName) {
        return writeFile(content, loadFile(fileName));
    }

    /**
     * 将内容写入到文件，默认向文件末尾追加内容
     *
     * @param content .
     * @param file    .
     * @return .
     */
    public static boolean writeFile(String content, File file) {
        return writeFile(content, file, true);
    }

    /**
     * 将内容写入到文件，如果文件已存在，按指定方式决定是追加还是覆盖
     *
     * @param content 待写内容
     * @param file    文件
     * @param append  是否将内容添加到文件末尾
     * @return 操作是否成功
     */
    public static boolean writeFile(String content, File file, boolean append) {
        if (Objs.isEmpty(content))
            throw new RuntimeException(OUT_CONTENT_NULL);
        return writeFile(content.getBytes(), file, append);
    }

    /**
     * 将字节数据组写入到文件中，如果文件已存在，按指定方式决定是追加还是覆盖，使用系统默认编码
     *
     * @param content 待写内容
     * @param file    文件
     * @param append  是否将内容添加到文件末尾
     * @return 操作是否成功
     */
    public static boolean writeFile(byte[] content, File file, boolean append) {
        if (Objs.isEmpty(file))
            throw new RuntimeException("要写入的文件不能为空");
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file, append);
        } catch (FileNotFoundException e) {
            proException("创建文件输出流对象失败 - 相关路径不存在", e);
        }
        try {
            assert fos != null : "Assert : 文件输出流对象为空";
            fos.write(content);
        } catch (IOException e) {
            proException("写文件失败", e);
        }
        return true;
    }

    /**
     * 将字符串写入到文件中，如果文件已存在，默认追加内容到末尾，使用系统默认编码
     *
     * @param content  待写内容
     * @param fileName 完整 文件名
     * @return 操作是否成功
     */
    public static boolean writeFileByCharset(String content, String fileName) {
        return writeFileByCharset(content, loadFile(fileName), true);
    }

    /**
     * 将字符串写入到文件中，如果文件已存在，默认追加内容到末尾，指定编码方式
     *
     * @param content 待写内容
     * @param file    将要写入的文件
     * @return 操作是否成功
     */
    public static boolean writeFileByCharset(String content, File file, String charset) {
        return writeFileByCharset(content, file, true, charset);
    }

    /**
     * 将字符串写入到文件中，如果文件已存在，指定是否追加内容到末尾，默认使用UTF-8编码方式
     *
     * @param content 待写内容
     * @param file    将要写入的文件
     * @return 操作是否成功
     */
    public static boolean writeFileByCharset(String content, File file, boolean append) {
        return writeFileByCharset(content, file, append, "utf-8");
    }

    /**
     * 将字符串写入到文件中，如果文件已存在，指定是否追加内容到末尾，指定编码方式
     *
     * @param content 待写内容
     * @param file    将要写入的文件
     * @return 操作是否成功
     */
    public static boolean writeFileByCharset(String content, File file, boolean append, String charset) {
        if (Objs.isEmpty(content))
            throw new RuntimeException(OUT_CONTENT_NULL);
        return writeFileByCharset(content.toCharArray(), file, append, charset);
    }

    /**
     * 将字节数组写入到文件中，如果文件已存在，指定是否追加内容到末尾，默认使用UTF-8编码方式
     *
     * @param content 待写内容
     * @param file    将要写入的文件
     * @return 操作是否成功
     */
    public static boolean writeFileByCharset(char[] content, File file, boolean append, String charset) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file, append);
        } catch (FileNotFoundException e) {
            proException("路径不存在", e);
        }

        OutputStreamWriter osw = null;
        try {
            assert fos != null : "FileOutputStream is null";
            osw = new OutputStreamWriter(fos, charset);
        } catch (UnsupportedEncodingException e) {
            proException("不被支持的编码格式", e);
        }
        assert osw != null : "OutputStreamWriter is null";
        BufferedWriter writer = new BufferedWriter(osw);
        try {
            writer.write(content);
        } catch (IOException e) {
            proException("写文件失败", e);
        }
        Closes.close(writer);
        return true;
    }

    /**
     * 默认：用utf-8读取整个文件到String中
     *
     * @param fileName 完整文件名
     * @return 字符串
     */
    public static String read(String fileName) {
        if (Objs.isEmpty(fileName))
            throw new RuntimeException("要读取的文件不能为空");
        return readToStringByCharset(fileName);
    }

    /**
     * 默认：用utf-8读取整个文件到String中
     *
     * @param file 要操作的文件
     * @return .
     */
    public static String read(File file) {
        if (Objs.isEmpty(file))
            throw new RuntimeException("要读取的文件不能为空");
        return readToStringByCharset(file);
    }

    /**
     * @param content 待写内容
     * @param file    要操作的文件
     * @return isSuccess
     */
    public static boolean write(String content, File file) {
        if (Objs.isEmpty(content))
            throw new RuntimeException(OUT_CONTENT_NULL);
        return writeFileByCharset(content, file, true, "utf-8");
    }

    /**
     * 将内容写入文件
     */
    public static boolean write(String content, String fileName) {
        return write(content, loadFile(fileName));
    }

}
